<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
    <head>
        <!-- Book generated using mdBook -->
        <meta charset="UTF-8">
        <title>Working with strings in the windows crate - Kenny Kerr</title>


        <!-- Custom HTML head -->
        <meta property="og:image" content="https://kennykerr.ca/image.jpg"/>

        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="theme-color" content="#ffffff">

        <link rel="icon" href="../favicon.svg">
        <link rel="stylesheet" href="../css/variables.css">
        <link rel="stylesheet" href="../css/general.css">
        <link rel="stylesheet" href="../css/chrome.css">
        <link rel="stylesheet" href="../css/print.css" media="print">

        <!-- Fonts -->
        <link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
        <link rel="stylesheet" href="../fonts/fonts.css">

        <!-- Highlight.js Stylesheets -->
        <link rel="stylesheet" id="highlight-css" href="../highlight.css">
        <link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
        <link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">

        <!-- Custom theme stylesheets -->


        <!-- Provide site root and default themes to javascript -->
        <script>
            const path_to_root = "../";
            const default_light_theme = "light";
            const default_dark_theme = "navy";
        </script>
        <!-- Start loading toc.js asap -->
        <script src="../toc.js"></script>
    </head>
    <body>
    <div id="mdbook-help-container">
        <div id="mdbook-help-popup">
            <h2 class="mdbook-help-title">Keyboard shortcuts</h2>
            <div>
                <p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
                <p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
                <p>Press <kbd>?</kbd> to show this help</p>
                <p>Press <kbd>Esc</kbd> to hide this help</p>
            </div>
        </div>
    </div>
    <div id="body-container">
        <!-- Work around some values being stored in localStorage wrapped in quotes -->
        <script>
            try {
                let theme = localStorage.getItem('mdbook-theme');
                let sidebar = localStorage.getItem('mdbook-sidebar');

                if (theme.startsWith('"') && theme.endsWith('"')) {
                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
                }

                if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
                }
            } catch (e) { }
        </script>

        <!-- Set the theme before any content is loaded, prevents flash -->
        <script>
            const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
            let theme;
            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
            if (theme === null || theme === undefined) { theme = default_theme; }
            const html = document.documentElement;
            html.classList.remove('light')
            html.classList.add(theme);
            html.classList.add("js");
        </script>

        <input type="checkbox" id="sidebar-toggle-anchor" class="hidden">

        <!-- Hide / unhide sidebar before it is displayed -->
        <script>
            let sidebar = null;
            const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
            if (document.body.clientWidth >= 1080) {
                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
                sidebar = sidebar || 'visible';
            } else {
                sidebar = 'hidden';
            }
            sidebar_toggle.checked = sidebar === 'visible';
            html.classList.remove('sidebar-visible');
            html.classList.add("sidebar-" + sidebar);
        </script>

        <nav id="sidebar" class="sidebar" aria-label="Table of contents">
            <!-- populated by js -->
            <mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
            <noscript>
                <iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
            </noscript>
            <div id="sidebar-resize-handle" class="sidebar-resize-handle">
                <div class="sidebar-resize-indicator"></div>
            </div>
        </nav>

        <div id="page-wrapper" class="page-wrapper">

            <div class="page">
                <div id="menu-bar-hover-placeholder"></div>
                <div id="menu-bar" class="menu-bar sticky">
                    <div class="left-buttons">
                        <label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </label>
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                        <button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                    </div>

                    <h1 class="menu-title">Kenny Kerr</h1>

                    <div class="right-buttons">
                        <a href="../print.html" title="Print this book" aria-label="Print this book">
                            <i id="print-button" class="fa fa-print"></i>
                        </a>
                        <a href="https://github.com/kennykerr/blog" title="Git repository" aria-label="Git repository">
                            <i id="git-repository-button" class="fa fa-github"></i>
                        </a>

                    </div>
                </div>

                <div id="search-wrapper" class="hidden">
                    <form id="searchbar-outer" class="searchbar-outer">
                        <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                    </form>
                    <div id="searchresults-outer" class="searchresults-outer hidden">
                        <div id="searchresults-header" class="searchresults-header"></div>
                        <ul id="searchresults">
                        </ul>
                    </div>
                </div>

                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
                <script>
                    document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                    document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                    Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                    });
                </script>

                <div id="content" class="content">
                    <main>
                        <h1 id="working-with-strings-in-the-windows-crate"><a class="header" href="#working-with-strings-in-the-windows-crate">Working with strings in the windows crate</a></h1>
<p>There are several string types in the Windows APIs including:</p>
<ul>
<li><code>PSTR</code>/<code>PCSTR</code>: A pointer to a null terminated string consisting of chars (u8). Strings should be encoded using the current thread's code page. A 'C' indicates a "constant" (read-only) string.</li>
<li><code>PWSTR</code>/<code>PCWSTR</code>: A pointer to a null terminated string consisting of 'wide chars' (u16), encoded using UTF-16.</li>
<li><code>BSTR</code>: A binary string commonly used in COM/OLE functions. It consists of u16 characters followed by a null terminator. The strings have their length prepended before the pointer, and some functions will use them to pass arbitrary binary data (including data containing nulls), relying on the prefix rather than the terminator. However, they <em>usually</em> can be used as normal, null-terminated wide strings.</li>
<li><code>HSTRING</code>: A handle to a Windows Runtime string. HSTRINGS are UTF-16 and immutable.</li>
</ul>
<p>Note that you can pass BSTR or HSTRING to functions expecting a PCWSTR.</p>
<p>Unfortunately, none of these types match Rust types one-to-one. However, we can use the <code>windows-strings</code> crate to help us.</p>
<h2 id="types-of-api-functions-narrow-or-wide"><a class="header" href="#types-of-api-functions-narrow-or-wide">Types of API Functions (Narrow or Wide)</a></h2>
<p>The Win32 API divides string functions into their "narrow" version (ending in 'A', like <code>MessageBoxA</code>), and their "wide" version (ending in 'W', like <code>MessageBoxW</code>). Narrow versions of the API expect u8 byte strings, encoded using the current thread's code page, while wide versions expect UTF-16.</p>
<p>As a general recommendation, you should prefer wide versions; it's much easier to convert between Rust's UTF-8 strings and Windows' UTF-16.</p>
<h2 id="calling-apis-that-consume-strings"><a class="header" href="#calling-apis-that-consume-strings">Calling APIs that Consume Strings</a></h2>
<p>Let's look at an example using the simple <a href="https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/UI/WindowsAndMessaging/fn.MessageBoxW.html"><code>MessageBox</code> function</a>, which displays a pop up dialog box. We'll use the wide version (<code>MessageBoxW</code>) for this example.</p>
<p>If you want to call a Windows API using a string literal, the <code>windows-strings</code> crate has macros to generate Windows strings from string literals:</p>
<ul>
<li><code>h!</code> generates an HSTRING from a string literal, adding a null terminator and converting to UTF-16.</li>
<li><code>w!</code> does the same, but generates a PCWSTR instead of an HSTRING.</li>
<li><code>s!</code> generates a PCSTR with a null terminator. <em>Caution: this does not do any conversions, it simply adds a null terminator.</em></li>
</ul>
<p>If we wanted to call the message box we could use the windows crate with the <code>Win32_UI_WindowsAndMessaging</code> feature, and call:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// use string literals when calling a message box. 
let text = h!("Hello from rust!");
let caption = h!("From Rust");

unsafe {
    // call the MessageBox function and return MESSAGEBOX_RESULT
    UI::WindowsAndMessaging::MessageBoxW(None, 
        text, 
        caption,
        UI::WindowsAndMessaging::MESSAGEBOX_STYLE(0) // message box OK
    )
}
<span class="boring">}</span></code></pre></pre>
<p>This works, but what if we wanted to call the same function with a Rust string? That gets slightly more complicated. We could manually convert to a UTF-16 series of bytes, and add the null terminator ourselves, like this:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// this works for any &amp;str, not just literals
let text = "I am a message to display!";
let caption = "Message from Rust!";

// convert our text and caption to UTF-16 bytes,
// add null terminators using chain, and then collect
// the result into a vec
let text = text.encode_utf16()
    .chain(iter::once(0u16))
    .collect::&lt;Vec&lt;u16&gt;&gt;();
let caption = caption.encode_utf16()
    .chain(iter::once(0u16))
    .collect::&lt;Vec&lt;u16&gt;&gt;();

// call the API, wrapping our vec pointer in a PCWSTR struct.
unsafe {
    UI::WindowsAndMessaging::MessageBoxW(None, 
        PCWSTR(text.as_ptr()), 
        PCWSTR(caption.as_ptr()),
        UI::WindowsAndMessaging::MESSAGEBOX_STYLE(0) // message box OK
    )
}
<span class="boring">}</span></code></pre></pre>
<p>However, this is cumbersome - we can use the convenience features in the <code>windows-strings</code> crate to make this much simpler by converting the Rust strings to HSTRING.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let text = "I am a message to display!";
let caption = "Message from Rust!";

// convert our strings into UTF-16 
// this incurrs a performance cost because there is a copy + conversion
// from the standard rust utf-8 string. 

// we are using HSTRING, which is an immutable UTF-16 string
// in the windows-strings crate. It can be generated from a standard
// rust string, and it can be used in place of a PCWSTR anywhere in the
// windows API. 

unsafe {
    UI::WindowsAndMessaging::MessageBoxW(None, 
        &amp;HSTRING::from(text), 
        &amp;HSTRING::from(caption),
        UI::WindowsAndMessaging::MESSAGEBOX_STYLE(0) // message box OK
    )
    }
<span class="boring">}</span></code></pre></pre>
<p>This is much more ergonomic - it handles the null termination and UTF-16 conversion for you.</p>
<h2 id="calling-apis-that-generate-strings"><a class="header" href="#calling-apis-that-generate-strings">Calling APIs that Generate Strings</a></h2>
<p>Windows APIs that generate strings usually require a two-step call. The first time you call the API, you pass in a NULL pointer for the string buffer, and retrieve the length of the string to be generated.</p>
<p>This allows you to allocate the buffer accordingly, and then call the function again with an appopriately sized buffer.</p>
<p>For this example, we'll use the <a href="https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/WindowsProgramming/fn.GetComputerNameW.html"><code>GetComputerNameW</code> function</a>. This requires the <code>Win32_System_WindowsProgramming</code> feature from the windows crate.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let mut buff_len = 0u32;

unsafe {
    // this function will return an error code because it
    // did not actually write the string. This is normal.
    let e = GetComputerNameW(None, &amp;mut buff_len).unwrap_err();
    debug_assert_eq!(e.code(), HRESULT::from(ERROR_BUFFER_OVERFLOW));
}

// buff len now has the length of the string (in UTF-16 characters)
// the function would like to write. This *does include* the
// null terminator. Let's create a vector buffer and feed that to the function.
let mut buffer = Vec::&lt;u16&gt;::with_capacity(buff_len as usize);

unsafe {
    WindowsProgramming::GetComputerNameW(
        Some(PWSTR(buffer.as_mut_ptr())), 
        &amp;mut buff_len).unwrap();

    // set the vector length
    // buff_len now includes the size, which *does not include* the null terminator.
    // let's set the length to just before the terminator so we don't have to worry
    // about it in later conversions.
    buffer.set_len(buff_len);
}

// we can now convert this to a valid Rust string
// omitting the null terminator
String::from_utf16_lossy(&amp;buffer)
<span class="boring">}</span></code></pre></pre>
<p>It's worth calling out how the length parameter works. For GetComputerNameW:</p>
<ul>
<li>On input, it represents the size of the buffer <em>including the null terminator</em> in wchar.</li>
<li>If the function returns a buffer overflow, the length parameter returned is how
large a buffer it needs <em>including the null terminator</em> in wchars.</li>
<li>If the function successfully wrote to the buffer, the length is the number
of wchars written <em>not including the null terminator.</em></li>
</ul>
<p><a href="https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getcomputernamew">This behavior is documented in the function's documentation</a> - when using the Windows API, be careful and check what the function expectes with respect to null terminators.</p>
<p>Regardless, this does work, but we can do better. Computer names can only be up to <code>MAX_COMPUTERNAME_LENGTH</code> which is a meager 16 characters. We can avoid a heap allocation here and just use arrays, since we know our buffer length at compile time.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// avoid the heap allocation since we already know how big this 
// buffer needs to be at compile time. 

let mut name = [0u16; MAX_COMPUTERNAME_LENGTH as usize + 1];
let mut len = name.len() as u32;

// we can also skip the two-step call, since we know our buffer
// is already larger than any possible computer name

unsafe {
    GetComputerNameW(
        Some(PWSTR(name.as_mut_ptr())), 
        &amp;mut len)
        .unwrap();
}

// the function writes to len with the number of 
// UTF-16 characters in the string. We can use this
// to slice the buffer. 
String::from_utf16_lossy(&amp;name[..len as usize])
<span class="boring">}</span></code></pre></pre>
<p>However, if we don't mind the heap allocation (and a few extra system calls), there is a more ergonomic option. The <code>windows-strings</code> crate includes <code>HStringBuilder</code> that we can use in place of the array. This gives us much easier conversions.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// pre-allocate a HSTRING buffer on the heap
// (you do not need to add one to len for the null terminator,
// the hstring builder will handle that automatically)

let mut buffer = HStringBuilder::new(
    MAX_COMPUTERNAME_LENGTH as usize);

let mut len = buffer.len() as u32 + 1;

unsafe {
    GetComputerNameW(
        Some(PWSTR(buffer.as_mut_ptr())), 
        &amp;mut len).unwrap();
}

// we can now generate a valid HSTRING from the HStringBuilder
let buffer = HSTRING::from(buffer);

// and we can now return a rust string from the HSTRING:
buffer.to_string_lossy()
<span class="boring">}</span></code></pre></pre>
<p>If you need to work with UTF-16 strings directly, consider using the <code>widestring</code> crate, which is UTF-16 aware. This will enable you to push/pop/append elements without having to convert the string to a native rust UTF-8 string. For completeness, here's an example of returning a widestring, and appending some exclaimation marks.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// for this example, we'll just use an array again

let mut name = [0u16; MAX_COMPUTERNAME_LENGTH as usize + 1];
let mut len = name.len() as u32;

unsafe {
    GetComputerNameW(
        Some(PWSTR(name.as_mut_ptr())), 
        &amp;mut len)
        .unwrap();
}

// we can make a UTF16Str slice directly from the buffer,
// without needing to do any copy. This will error if the buffer
// isn't valid UTF-16. 
let wstr = Utf16Str::from_slice(&amp;name[..len as usize])
    .unwrap();

// this can be displayed as is.
println!("Computer name is {}", wstr);

// we can also transfer it into owned string, which can
// be appended or modified. 
let mut wstring = Utf16String::from(wstr);

// let's append another string. We'll use a macro to avoid
// any UTF conversion at runtime. 
wstring = wstring + utf16str!("!!!");
<span class="boring">}</span></code></pre></pre>

                    </main>

                    <nav class="nav-wrapper" aria-label="Page navigation">
                        <!-- Mobile navigation buttons -->
                            <a rel="prev" href="../rust-getting-started/implement-win32-api.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                                <i class="fa fa-angle-left"></i>
                            </a>


                        <div style="clear: both"></div>
                    </nav>
                </div>
            </div>

            <nav class="nav-wide-wrapper" aria-label="Page navigation">
                    <a rel="prev" href="../rust-getting-started/implement-win32-api.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                        <i class="fa fa-angle-left"></i>
                    </a>

            </nav>

        </div>




        <script>
            window.playground_copyable = true;
        </script>


        <script src="../elasticlunr.min.js"></script>
        <script src="../mark.min.js"></script>
        <script src="../searcher.js"></script>

        <script src="../clipboard.min.js"></script>
        <script src="../highlight.js"></script>
        <script src="../book.js"></script>

        <!-- Custom JS scripts -->


    </div>
    </body>
</html>
